//+------------------------------------------------------------------+
//|                                                BollingerBandEA.mq5 |
//|                                                                     |
//|                                                                     |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024"
#property link      ""
#property version   "1.00"

// 外部参数
input int      BB_Period = 20;                // 布林带周期
input double   BB_Deviation = 2.0;            // 布林带标准差倍数
input ENUM_APPLIED_PRICE BB_PriceType = PRICE_CLOSE; // 布林带价格类型
input double   InitialLot = 0.1;              // 初始手数
input int      Magic = 123456;                // EA魔术号
input double   StopLossATR = 2.0;             // 固定止损倍数
input int      ATRPeriod = 14;                // ATR周期
input int      TrailCandleCount = 3;          // 连续K线数（移动止损）
input double   MartinFactor = 2.0;             // 微马丁倍率（马丁系数）
input double   InitialStopATR = 0.5;          // 初始止损ATR倍数
input double   TrailingStepATR = 0.2;         // 移动止损间距ATR倍数
input int      BB_TrendPeriod = 20;    // 布林带趋势判断周期
input double   BB_WidthThreshold = 0.2; // 布林带开口阈值
input double   BB_SlopeThreshold = 0.1; // 布林带斜率阈值

// 全局变量
int g_bbHandle;                       // 布林带指标句柄
double g_bbUpperBuffer[];             // 布林带上轨
double g_bbMiddleBuffer[];            // 布林带中轨
double g_bbLowerBuffer[];             // 布林带下轨
datetime g_lastChartTime;             // 上次图表更新时间
int g_buyStopCount = 0;               // 买单止损次数
int g_sellStopCount = 0;              // 卖单止损次数
int g_atrHandle;                      // ATR指标句柄
double g_atrBuffer[];                 // ATR缓冲区
int g_totalBuyCount = 0;
double g_totalBuyProfit = 0.0;
int g_totalSellCount = 0;
double g_totalSellProfit = 0.0;
datetime g_lastBuyOpenTime = 0;
datetime g_lastSellOpenTime = 0;
datetime g_lastBarTime = 0;           // 记录上一次K线的时间
int g_lastExpansionState = 0;         // 0=无, 1=扩大, 2=收窄

// 布林带开口可视化相关变量
bool g_isExpanding = false;           // 当前是否处于开口状态
datetime g_expansionStartTime = 0;     // 开口开始时间
double g_lastWidth = 0;               // 上一次的带宽
int g_expansionCount = 0;             // 开口持续计数

//+------------------------------------------------------------------+
//| 计算布林带宽度变化率 - 使用多个数学模型                          |
//+------------------------------------------------------------------+
double CalculateBBWidthChange()
{
    double bbUpper[], bbLower[], bbMiddle[];
    ArraySetAsSeries(bbUpper, true);
    ArraySetAsSeries(bbLower, true);
    ArraySetAsSeries(bbMiddle, true);
    
    // 获取最近N个周期的布林带数据
    int lookback = 10;  // 回看周期数
    if(CopyBuffer(g_bbHandle, 1, 0, lookback, bbUpper) < lookback) return 0;
    if(CopyBuffer(g_bbHandle, 2, 0, lookback, bbLower) < lookback) return 0;
    if(CopyBuffer(g_bbHandle, 0, 0, lookback, bbMiddle) < lookback) return 0;
    
    // 1. 计算带宽的指数加权移动平均(EWMA)
    double alpha = 0.2;  // EWMA平滑因子
    double ewmaWidth = 0;
    double sumWeight = 0;
    
    for(int i = 0; i < lookback; i++)
    {
        double width = bbUpper[i] - bbLower[i];
        double weight = MathPow(1 - alpha, i);
        ewmaWidth += width * weight;
        sumWeight += weight;
    }
    ewmaWidth /= sumWeight;
    
    // 2. 计算带宽的标准差
    double sumSquaredDiff = 0;
    for(int i = 0; i < lookback; i++)
    {
        double width = bbUpper[i] - bbLower[i];
        double diff = width - ewmaWidth;
        sumSquaredDiff += diff * diff;
    }
    double widthStdDev = MathSqrt(sumSquaredDiff / lookback);
    
    // 3. 计算带宽的加速度（二阶导数）
    double currentWidth = bbUpper[0] - bbLower[0];
    double prevWidth = bbUpper[1] - bbLower[1];
    double prevPrevWidth = bbUpper[2] - bbLower[2];
    
    double firstDerivative = (currentWidth - prevWidth) / prevWidth;
    double prevFirstDerivative = (prevWidth - prevPrevWidth) / prevPrevWidth;
    double acceleration = (firstDerivative - prevFirstDerivative) / prevFirstDerivative;
    
    // 4. 计算带宽的波动率
    double volatility = 0;
    for(int i = 1; i < lookback; i++)
    {
        double width = bbUpper[i] - bbLower[i];
        double prevWidth = bbUpper[i-1] - bbLower[i-1];
        volatility += MathAbs((width - prevWidth) / prevWidth);
    }
    volatility /= (lookback - 1);
    
    // 5. 综合评分计算
    double score = 0;
    
    // 带宽变化率权重
    double widthChangeWeight = 0.3;
    double widthChange = (currentWidth - prevWidth) / prevWidth;
    score += widthChange * widthChangeWeight;
    
    // 标准差变化权重
    double stdDevWeight = 0.2;
    double stdDevChange = widthStdDev / ewmaWidth;
    score += stdDevChange * stdDevWeight;
    
    // 加速度权重
    double accelerationWeight = 0.2;
    score += acceleration * accelerationWeight;
    
    // 波动率权重
    double volatilityWeight = 0.3;
    score += volatility * volatilityWeight;
    
    // 归一化处理
    score = MathMax(-1, MathMin(1, score));
    
    return score;
}

//+------------------------------------------------------------------+
//| 计算布林带中轨斜率                                                |
//+------------------------------------------------------------------+
double CalculateBBSlope()
{
    double bbMiddle[];
    ArraySetAsSeries(bbMiddle, true);
    
    // 获取最近两个周期的中轨数据
    if(CopyBuffer(g_bbHandle, 0, 0, 2, bbMiddle) < 2) return 0;
    
    // 计算斜率（价格变化率）
    return (bbMiddle[0] - bbMiddle[1]) / bbMiddle[1];
}

//+------------------------------------------------------------------+
//| 判断布林带走势                                                    |
//+------------------------------------------------------------------+
int AnalyzeBBTrendAndReturnState()
{
    // 计算布林带宽度变化率
    double widthChange = CalculateBBWidthChange();
    
    // 计算布林带中轨斜率
    double slope = CalculateBBSlope();
    
    // 获取当前布林带宽度
    double bbUpper[], bbLower[];
    ArraySetAsSeries(bbUpper, true);
    ArraySetAsSeries(bbLower, true);
    
    if(CopyBuffer(g_bbHandle, 1, 0, 1, bbUpper) <= 0) return 0;
    if(CopyBuffer(g_bbHandle, 2, 0, 1, bbLower) <= 0) return 0;
    
    double currentWidth = bbUpper[0] - bbLower[0];
    
    // 判断布林带开口
    bool wasExpanding = g_isExpanding;
    g_isExpanding = MathAbs(widthChange) > BB_WidthThreshold;
    Print("布林带宽度变化率: ", widthChange, " 是否开口: ", g_isExpanding);       
    
    // 更新开口状态
    if(g_isExpanding)
    {
        if(!wasExpanding)
        {
            // 开始新的开口状态
            g_expansionStartTime = iTime(_Symbol, PERIOD_CURRENT, 1);
            g_expansionCount = 1;
            Print("布林带开始开口，开始时间：", TimeToString(g_expansionStartTime));
        }
        else
        {
            g_expansionCount++;
        }
    }
    else
    {
        if(wasExpanding && g_expansionCount >= 3)  // 最小持续3根K线
        {
            // 开口状态结束，绘制区域
            datetime endTime = iTime(_Symbol, PERIOD_CURRENT, 1);
            Print("布林带开口结束，开始时间：", TimeToString(g_expansionStartTime), " 结束时间：", TimeToString(endTime));
            DrawExpansionZone(g_expansionStartTime, endTime);
        }
        g_expansionCount = 0;
    }
    
    // 更新上一次的宽度
    g_lastWidth = currentWidth;
    
    // 判断布林带倾斜
    bool isUpTrend = slope > BB_SlopeThreshold;
    bool isDownTrend = slope < -BB_SlopeThreshold;
    
    // 创建标签对象名称
    string labelName = "BB_Trend_Label";
    
    // 删除旧的标签
    ObjectDelete(0, labelName);
    
    // 创建新的标签
    if(!ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0))
    {
        Print("创建标签失败: ", GetLastError());
        return 0;
    }
    
    // 设置标签属性
    ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10);
    ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 30);
    ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10);
    ObjectSetString(0, labelName, OBJPROP_FONT, "Arial");
    ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrWhite);
    ObjectSetInteger(0, labelName, OBJPROP_BACK, false);
    ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, labelName, OBJPROP_SELECTED, false);
    ObjectSetInteger(0, labelName, OBJPROP_HIDDEN, true);
    
    // 构建标签文本
    string labelText = "布林带分析:\n";
    bool hasSignificantChange = false;
    int expansionState = 0;
    
    if(g_isExpanding)
    {
        hasSignificantChange = true;
        if(widthChange > 0)
        {
            labelText += "开口扩大 - 可能即将出现大行情\n";
            expansionState = 1;
        }
        else
        {
            labelText += "开口收窄 - 可能即将出现盘整\n";
            expansionState = 2;
        }
    }
    
    if(isUpTrend)
    {
        hasSignificantChange = true;
        labelText += "向上倾斜 - 上升趋势";
    }
    else if(isDownTrend)
    {
        hasSignificantChange = true;
        labelText += "向下倾斜 - 下降趋势";
    }
    
    // 只有在有显著变化时才显示标签和打印日志
    if(hasSignificantChange)
    {
        // 设置标签文本
        ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
        // 输出日志
        Print(labelText);
    }
    else
    {
        // 如果没有显著变化，删除标签
        ObjectDelete(0, labelName);
    }
    
    return expansionState;
}

//+------------------------------------------------------------------+
//| 绘制开口区域                                                      |
//+------------------------------------------------------------------+
void DrawExpansionZone(datetime startTime, datetime endTime)
{
    string objName = "BB_Expansion_" + TimeToString(startTime);
    
    // 删除已存在的对象
    ObjectDelete(0, objName);
    
    // 获取价格范围
    double high = iHigh(_Symbol, PERIOD_CURRENT, iBarShift(_Symbol, PERIOD_CURRENT, startTime));
    double low = iLow(_Symbol, PERIOD_CURRENT, iBarShift(_Symbol, PERIOD_CURRENT, endTime));
    
    // 创建矩形对象
    if(!ObjectCreate(0, objName, OBJ_RECTANGLE, 0, startTime, high, endTime, low))
    {
        Print("创建开口区域对象失败: ", GetLastError());
        return;
    }
    
    // 设置对象属性
    ObjectSetInteger(0, objName, OBJPROP_COLOR, C'128,128,128');
    ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_SOLID);
    ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1);
    ObjectSetInteger(0, objName, OBJPROP_FILL, true);
    ObjectSetInteger(0, objName, OBJPROP_BACK, true);
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
    ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
    ObjectSetInteger(0, objName, OBJPROP_ZORDER, 0);
    
    // 强制更新图表
    ChartRedraw();
}

//+------------------------------------------------------------------+
//| 下单函数                                                         |
//+------------------------------------------------------------------+
void TryOpenOrder()
{
    // 获取当前K线数据
    MqlRates rates[1];
    ArraySetAsSeries(rates, true);
    if(CopyRates(_Symbol, PERIOD_CURRENT, 0, 1, rates) < 1) return;

    // 获取布林带数据
    double bbUpper[1], bbLower[1];
    ArraySetAsSeries(bbUpper, true);
    ArraySetAsSeries(bbLower, true);
    if(CopyBuffer(g_bbHandle, 1, 0, 1, bbUpper) < 1) return;
    if(CopyBuffer(g_bbHandle, 2, 0, 1, bbLower) < 1) return;

    // 获取ATR数据
    double atr = 0.0;
    if(g_atrHandle != INVALID_HANDLE) {
        double atrArr[1];
        ArraySetAsSeries(atrArr, true);
        if(CopyBuffer(g_atrHandle, 0, 0, 1, atrArr) >= 1)
            atr = atrArr[0];
    }
    if(atr <= 0) return;

    // 检查是否已有本EA持仓
    bool hasPosition = false;
    for(int i=0; i<PositionsTotal(); i++) {
        ulong ticket = PositionGetTicket(i);
        if(PositionSelectByTicket(ticket)) {
            if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
               PositionGetInteger(POSITION_MAGIC) == Magic) {
                hasPosition = true;
                break;
            }
        }
    }
    if(hasPosition) return;

    // 计算马丁手数
    double buyLot = InitialLot * MathPow(MartinFactor, MathMin(g_buyStopCount, 4));
    buyLot = NormalizeDouble(buyLot, 2);
    double sellLot = InitialLot * MathPow(MartinFactor, MathMin(g_sellStopCount, 4));
    sellLot = NormalizeDouble(sellLot, 2);

    // 计算自适应开仓间隔（秒）
    int periodSec = PeriodSeconds();
    int minIntervalSec = periodSec * 2;
    datetime now = TimeCurrent();

    // 获取平台最小止损距离
    double minStopDist = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;

    double marketAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double marketBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

    // 买单逻辑：当前K线为阳线，且开盘价<下轨，当前市场价>下轨
    if(rates[0].open < bbLower[0] && marketAsk > bbLower[0] && marketAsk > rates[0].open) {
        if(now - g_lastBuyOpenTime >= minIntervalSec) {
            MqlTradeRequest req = {};
            MqlTradeResult res = {};
            req.action = TRADE_ACTION_DEAL;
            req.symbol = _Symbol;
            req.volume = buyLot;
            req.type = ORDER_TYPE_BUY;
            req.price = marketAsk;
            req.deviation = 10;
            req.magic = Magic;
            req.type_filling = ORDER_FILLING_FOK;
            // 止损应低于买入价
            double sl = req.price - StopLossATR * atr;
            if((req.price - sl) < minStopDist) sl = req.price - minStopDist;
            req.sl = NormalizeDouble(sl, _Digits);
            PrintFormat("买单下单: 价格=%.5f, 止损=%.5f, 最小止损距离=%.5f", req.price, req.sl, minStopDist);
            OrderSend(req, res);
            g_lastBuyOpenTime = now;
        }
    }

    // 卖单逻辑：当前K线为阴线，且开盘价>上轨，当前市场价<上轨
    if(rates[0].open > bbUpper[0] && marketBid < bbUpper[0] && marketBid < rates[0].open) {
        if(now - g_lastSellOpenTime >= minIntervalSec) {
            MqlTradeRequest req = {};
            MqlTradeResult res = {};
            req.action = TRADE_ACTION_DEAL;
            req.symbol = _Symbol;
            req.volume = sellLot;
            req.type = ORDER_TYPE_SELL;
            req.price = marketBid;
            req.deviation = 10;
            req.magic = Magic;
            req.type_filling = ORDER_FILLING_FOK;
            // 止损应高于卖出价
            double sl = req.price + StopLossATR * atr;
            if((sl - req.price) < minStopDist) sl = req.price + minStopDist;
            req.sl = NormalizeDouble(sl, _Digits);
            PrintFormat("卖单下单: 价格=%.5f, 止损=%.5f, 最小止损距离=%.5f", req.price, req.sl, minStopDist);
            OrderSend(req, res);
            g_lastSellOpenTime = now;
        }
    }
}

//+------------------------------------------------------------------+
//| 移动止损检查函数                                                 |
//+------------------------------------------------------------------+
void CheckTrailingStop()
{
    for(int i=0; i<PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if(PositionSelectByTicket(ticket))
        {
            if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic)
            {
                int type = PositionGetInteger(POSITION_TYPE);
                double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
                datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
                int shift = iBarShift(_Symbol, PERIOD_CURRENT, openTime, true);
                if(shift < 0) continue;
                bool shouldClose = true;
                // 买单：连续M根阴线
                if(type == POSITION_TYPE_BUY)
                {
                    for(int j=0; j<TrailCandleCount; j++)
                    {
                        double open = iOpen(_Symbol, PERIOD_CURRENT, shift-j-1);
                        double close = iClose(_Symbol, PERIOD_CURRENT, shift-j-1);
                        if(close > open) { shouldClose = false; break; }
                    }
                }
                // 卖单：连续M根阳线
                if(type == POSITION_TYPE_SELL)
                {
                    for(int j=0; j<TrailCandleCount; j++)
                    {
                        double open = iOpen(_Symbol, PERIOD_CURRENT, shift-j-1);
                        double close = iClose(_Symbol, PERIOD_CURRENT, shift-j-1);
                        if(close < open) { shouldClose = false; break; }
                    }
                }
                if(shouldClose)
                {
                    // 平仓
                    MqlTradeRequest req = {};
                    MqlTradeResult res = {};
                    req.action = TRADE_ACTION_DEAL;
                    req.position = ticket;
                    req.symbol = _Symbol;
                    req.volume = PositionGetDouble(POSITION_VOLUME);
                    req.type = (type == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
                    req.price = (type == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
                    req.deviation = 10;
                    req.magic = Magic;
                    req.type_filling = ORDER_FILLING_FOK;
                    OrderSend(req, res);
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| 止盈检查与修改函数                                               |
//+------------------------------------------------------------------+
void CheckTakeProfit()
{
    double minTPDistance = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
    double atr = 0.0;
    if(g_atrHandle != INVALID_HANDLE) {
        double atrArr[2];
        ArraySetAsSeries(atrArr, true);
        if(CopyBuffer(g_atrHandle, 0, 0, 2, atrArr) >= 2)
            atr = atrArr[0];
    }
    if(atr <= 0) return;

    double bbUpper[2], bbLower[2], bbMiddle[2];
    ArraySetAsSeries(bbUpper, true);
    ArraySetAsSeries(bbLower, true);
    ArraySetAsSeries(bbMiddle, true);
    if(CopyBuffer(g_bbHandle, 1, 0, 2, bbUpper) < 2) return;
    if(CopyBuffer(g_bbHandle, 2, 0, 2, bbLower) < 2) return;
    if(CopyBuffer(g_bbHandle, 0, 0, 2, bbMiddle) < 2) return;

    for(int i=0; i<PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if(PositionSelectByTicket(ticket))
        {
            if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic)
            {
                int type = PositionGetInteger(POSITION_TYPE);
                double volume = PositionGetDouble(POSITION_VOLUME);
                double tp = PositionGetDouble(POSITION_TP);
                double price = (type == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK);

                // 1. 平仓止盈
                if(type == POSITION_TYPE_BUY && price >= bbUpper[0])
                {
                    Print(" 买单：price: ", price, " bbUpper[0]: ", bbUpper[0]);
                    // 买单穿越上轨，强制平仓
                    MqlTradeRequest req = {};
                    MqlTradeResult res = {};
                    req.action = TRADE_ACTION_DEAL;
                    req.position = ticket;
                    req.symbol = _Symbol;
                    req.volume = volume;
                    req.type = ORDER_TYPE_SELL;
                    req.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
                    req.deviation = 10;
                    req.magic = Magic;
                    req.type_filling = ORDER_FILLING_FOK;
                    OrderSend(req, res);
                    continue;
                }
        
                if(type == POSITION_TYPE_SELL && price <= bbLower[0])
                {
                    Print(" 卖单：price: ", price, " bbLower[0]: ", bbLower[0]);
                    // 卖单穿越下轨，强制平仓
                    MqlTradeRequest req = {};
                    MqlTradeResult res = {};
                    req.action = TRADE_ACTION_DEAL;
                    req.position = ticket;
                    req.symbol = _Symbol;
                    req.volume = volume;
                    req.type = ORDER_TYPE_BUY;
                    req.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
                    req.deviation = 10;
                    req.magic = Magic;
                    req.type_filling = ORDER_FILLING_FOK;
                    OrderSend(req, res);
                    continue;
                }

                // 2. 移动止损
                if(type == POSITION_TYPE_BUY && price > bbMiddle[0])
                {
                    double trailingStep = TrailingStepATR * atr;  // 移动止损间距
                    double newSL = price - InitialStopATR * atr; // 初始止损距离
                    double currentSL = PositionGetDouble(POSITION_SL);
                    
                    if(currentSL == 0)
                    {
                        // 初始止损设置
                        if((price - newSL) > minTPDistance)
                        {
                            Print(" 买单初始止损：price: ", price, " bbMiddle[0]: ", bbMiddle[0]);
                            MqlTradeRequest req = {};
                            MqlTradeResult res = {};
                            req.action = TRADE_ACTION_SLTP;
                            req.position = ticket;
                            req.symbol = _Symbol;
                            req.sl = NormalizeDouble(newSL, _Digits);
                            req.tp = PositionGetDouble(POSITION_TP);
                            req.magic = Magic;
                            OrderSend(req, res);
                        }
                    }
                    else
                    {
                        // 跟踪止损
                        if((price - currentSL) > trailingStep && (newSL - currentSL) > minTPDistance)
                        {
                            Print(" 买单移动止损：price: ", price, " bbMiddle[0]: ", bbMiddle[0]);
                            MqlTradeRequest req = {};
                            MqlTradeResult res = {};
                            req.action = TRADE_ACTION_SLTP;
                            req.position = ticket;
                            req.symbol = _Symbol;
                            req.sl = NormalizeDouble(newSL, _Digits);
                            req.tp = PositionGetDouble(POSITION_TP);
                            req.magic = Magic;
                            OrderSend(req, res);
                        }
                    }
                }
                if(type == POSITION_TYPE_SELL && price < bbMiddle[0])
                {
                    double trailingStep = TrailingStepATR * atr;  // 移动止损间距
                    double newSL = price + InitialStopATR * atr; // 初始止损距离
                    double currentSL = PositionGetDouble(POSITION_SL);
                    
                    if(currentSL == 0)
                    {
                        // 初始止损设置
                        if((newSL - price) > minTPDistance)
                        {
                            Print(" 卖单初始止损：price: ", price, " bbMiddle[0]: ", bbMiddle[0]);
                            MqlTradeRequest req = {};
                            MqlTradeResult res = {};
                            req.action = TRADE_ACTION_SLTP;
                            req.position = ticket;
                            req.symbol = _Symbol;
                            req.sl = NormalizeDouble(newSL, _Digits);
                            req.tp = PositionGetDouble(POSITION_TP);
                            req.magic = Magic;
                            OrderSend(req, res);
                        }
                    }
                    else
                    {
                        // 跟踪止损
                        if((currentSL - price) > trailingStep && (currentSL - newSL) > minTPDistance)
                        {
                            Print(" 卖单移动止损：price: ", price, " bbMiddle[0]: ", bbMiddle[0]);
                            MqlTradeRequest req = {};
                            MqlTradeResult res = {};
                            req.action = TRADE_ACTION_SLTP;
                            req.position = ticket;
                            req.symbol = _Symbol;
                            req.sl = NormalizeDouble(newSL, _Digits);
                            req.tp = PositionGetDouble(POSITION_TP);
                            req.magic = Magic;
                            OrderSend(req, res);
                        }
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Expert initialization function                                     |
//+------------------------------------------------------------------+
int OnInit()
{
    // 初始化布林带指标
    g_bbHandle = iBands(_Symbol, PERIOD_CURRENT, BB_Period, 0, BB_Deviation, BB_PriceType);
    if(g_bbHandle == INVALID_HANDLE)
    {
        Print("布林带指标初始化失败!");
        return INIT_FAILED;
    }
    // 初始化ATR指标
    g_atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod);
    if(g_atrHandle == INVALID_HANDLE)
    {
        Print("ATR指标初始化失败!");
        return INIT_FAILED;
    }
    // 分配数组内存
    ArraySetAsSeries(g_bbUpperBuffer, true);
    ArraySetAsSeries(g_bbMiddleBuffer, true);
    ArraySetAsSeries(g_bbLowerBuffer, true);
    ArraySetAsSeries(g_atrBuffer, true);
    // 初始化最后更新时间
    g_lastChartTime = 0;
    // 启用图表事件
    ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true);
    // CheckAndMarkCandles();
    // CheckHistoricalExpansions();  // 添加检查历史开口区域
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                   |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // 释放指标句柄
    if(g_bbHandle != INVALID_HANDLE)
        IndicatorRelease(g_bbHandle);
    if(g_atrHandle != INVALID_HANDLE)
        IndicatorRelease(g_atrHandle);
        
    // 删除所有标记
    ObjectsDeleteAll(0, "BB_");
    ObjectsDeleteAll(0, "BB_Trend_");  // 删除所有趋势标记
    ObjectsDeleteAll(0, "BB_Expansion_");  // 删除所有开口区域标记
}

//+------------------------------------------------------------------+
//| Expert tick function                                              |
//+------------------------------------------------------------------+
void OnTick()
{
    datetime lastBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
    if(lastBarTime != g_lastBarTime)
    {
        // 新K线形成，更新布林带分析
        int expansionState = AnalyzeBBTrendAndReturnState();
        
        // 检查开口状态是否发生变化
        if(expansionState != 0 && expansionState != g_lastExpansionState)
        {
            // 状态切换，报警
            if(expansionState == 1)
                Alert("布林带开口扩大 - 可能即将出现大行情");
            else if(expansionState == 2)
                Alert("布林带开口收窄 - 可能即将出现盘整");
        }
        
        // 更新状态
        g_lastExpansionState = expansionState;
        g_lastBarTime = lastBarTime;
    }
    
    // 其他交易逻辑
    TryOpenOrder();
    CheckTakeProfit();
    UpdateTradeStatsAndPanel();
}

//+------------------------------------------------------------------+
//| ChartEvent function                                               |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
    // 检查是否是图表变化事件
    if(id == CHARTEVENT_CHART_CHANGE)
    {
        // 获取当前图表时间
        datetime currentTime = (datetime)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
        
        // 如果图表时间发生变化，重新检查并标记
        if(currentTime != g_lastChartTime)
        {
            g_lastChartTime = currentTime;
            // CheckAndMarkCandles();
            // CheckHistoricalExpansions();  // 添加检查历史开口区域
        }
    }
}

//+------------------------------------------------------------------+
//| 检查并标记符合条件的K线                                           |
//+------------------------------------------------------------------+
void CheckAndMarkCandles()
{
    // 获取可见K线范围
    int firstVisibleBar = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
    int visibleBars = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
    
    // 获取K线数据 - 获取比可见区域更多的数据以确保完整标记
    MqlRates rates[];
    ArraySetAsSeries(rates, true);
    int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, firstVisibleBar + visibleBars + 100, rates);
    if(copied <= 0) return;
    
    // 更新布林带数据 - 确保复制足够的数据点
    if(CopyBuffer(g_bbHandle, 0, 0, copied, g_bbMiddleBuffer) <= 0) return;
    if(CopyBuffer(g_bbHandle, 1, 0, copied, g_bbUpperBuffer) <= 0) return;
    if(CopyBuffer(g_bbHandle, 2, 0, copied, g_bbLowerBuffer) <= 0) return;
    
    // 遍历所有K线
    for(int i = 1; i < copied; i++)  // 从1开始，因为0是未完成的K线
    {
        // 检查是否已经标记过这根K线
        string objName = "BB_" + TimeToString(rates[i].time);
        if(ObjectFind(0, objName) >= 0) continue;
        
        // 获取对应的布林带值
        double bbLower = g_bbLowerBuffer[i];
        double bbUpper = g_bbUpperBuffer[i];
        
        // 检查买入条件：阳线且从下向上穿过布林带下轨
        if(rates[i].close > rates[i].open &&  // 阳线
           rates[i].open < bbLower &&         // 开盘价在下轨下方
           rates[i].close > bbLower)          // 收盘价在下轨上方
        {
            // 绘制买入标记（绿色向上箭头）
            if(!ObjectCreate(0, objName, OBJ_ARROW_UP, 0, rates[i].time, rates[i].low))
            {
                Print("创建买入标记失败: ", GetLastError());
            }
            else
            {
                ObjectSetInteger(0, objName, OBJPROP_COLOR, clrLime);
                ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
                ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
                ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
                ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
                ObjectSetInteger(0, objName, OBJPROP_ZORDER, 2);
                ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
            }
        }
        
        // 检查卖出条件：阴线且从上向下穿过布林带上轨
        if(rates[i].close < rates[i].open &&  // 阴线
           rates[i].open > bbUpper &&         // 开盘价在上轨上方
           rates[i].close < bbUpper)          // 收盘价在上轨下方
        {
            // 绘制卖出标记（红色向下箭头）
            if(!ObjectCreate(0, objName, OBJ_ARROW_DOWN, 0, rates[i].time, rates[i].high))
            {
                Print("创建卖出标记失败: ", GetLastError());
            }
            else
            {
                ObjectSetInteger(0, objName, OBJPROP_COLOR, clrRed);
                ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
                ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
                ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
                ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
                ObjectSetInteger(0, objName, OBJPROP_ZORDER, 2);
                ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_TOP);
                ObjectSetDouble(0, objName, OBJPROP_PRICE, rates[i].high + 10 * _Point);  // 向上偏移10个点
            }
        }
    }
}

//+------------------------------------------------------------------+
//| 交易事务监听函数                                                  |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
{
    if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
    {
        ulong deal_ticket = trans.deal;
        if(deal_ticket > 0)
        {
            long reason = HistoryDealGetInteger(deal_ticket, DEAL_REASON);
            double profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT);
            long magic = HistoryDealGetInteger(deal_ticket, DEAL_MAGIC);
            string symbol = HistoryDealGetString(deal_ticket, DEAL_SYMBOL);
            int type = HistoryDealGetInteger(deal_ticket, DEAL_TYPE);

            if(magic == Magic && symbol == _Symbol)
            {
                if(reason == DEAL_REASON_SL)
                {
                    if(type == DEAL_TYPE_BUY)
                        g_buyStopCount++;
                    else if(type == DEAL_TYPE_SELL)
                        g_sellStopCount++;
                }
                // 盈利平仓归零
                if(profit > 0)
                {
                    if(type == DEAL_TYPE_BUY)
                    {
                        g_buyStopCount = 0;
                        g_totalBuyCount++;
                        g_totalBuyProfit += profit;
                    }
                    else if(type == DEAL_TYPE_SELL)
                    {
                        g_sellStopCount = 0;
                        g_totalSellCount++;
                        g_totalSellProfit += profit;
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| 统计并展示信息函数                                               |
//+------------------------------------------------------------------+
void UpdateTradeStatsAndPanel()
{
    int buyCount = 0, sellCount = 0;
    double buyProfit = 0, sellProfit = 0;
    double buyLots = 0, sellLots = 0;

    for(int i=0; i<PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if(PositionSelectByTicket(ticket))
        {
            if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic)
            {
                int type = PositionGetInteger(POSITION_TYPE);
                double profit = PositionGetDouble(POSITION_PROFIT);
                double lots = PositionGetDouble(POSITION_VOLUME);
                if(type == POSITION_TYPE_BUY)
                {
                    buyCount++;
                    buyProfit += profit;
                    buyLots += lots;
                }
                else if(type == POSITION_TYPE_SELL)
                {
                    sellCount++;
                    sellProfit += profit;
                    sellLots += lots;
                }
            }
        }
    }

    // 日志输出
    //PrintFormat("当前买单: %d, 卖单: %d, 买单收益: %.2f, 卖单收益: %.2f, 买单手数: %.2f, 卖单手数: %.2f", buyCount, sellCount, buyProfit, sellProfit, buyLots, sellLots);
    //PrintFormat("累计买单数: %d, 累计买单收益: %.2f, 累计卖单数: %d, 累计卖单收益: %.2f", g_totalBuyCount, g_totalBuyProfit, g_totalSellCount, g_totalSellProfit);

    // 右上角面板展示
    string panelName = "EA_STATS_PANEL";
    int x = 10, y = 10, step = 18;
    string text = StringFormat(
        "当前买单: %d  卖单: %d\n买单收益: %.2f  卖单收益: %.2f\n买单手数: %.2f  卖单手数: %.2f\n-----------------------------\n累计买单数: %d  累计买单收益: %.2f\n累计卖单数: %d  累计卖单收益: %.2f",
        buyCount, sellCount, buyProfit, sellProfit, buyLots, sellLots,
        g_totalBuyCount, g_totalBuyProfit, g_totalSellCount, g_totalSellProfit
    );
    if(ObjectFind(0, panelName) < 0)
    {
        ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0);
    }
    ObjectSetString(0, panelName, OBJPROP_TEXT, text);
    ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
    ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, y);
    ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE, 10);
    ObjectSetString(0, panelName, OBJPROP_FONT, "Consolas");
    ObjectSetInteger(0, panelName, OBJPROP_COLOR, clrWhite);
    ObjectSetInteger(0, panelName, OBJPROP_BACK, false);
    ObjectSetInteger(0, panelName, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, panelName, OBJPROP_SELECTED, false);
    ObjectSetInteger(0, panelName, OBJPROP_HIDDEN, true);
}

//+------------------------------------------------------------------+
//| 检查并绘制历史开口区域                                            |
//+------------------------------------------------------------------+
void CheckHistoricalExpansions()
{
    // 获取可见K线范围
    int firstVisibleBar = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
    int visibleBars = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
    
    // 获取布林带数据
    double bbUpper[], bbLower[];
    ArraySetAsSeries(bbUpper, true);
    ArraySetAsSeries(bbLower, true);
    
    // 获取比可见区域更多的数据以确保完整标记
    int copied = CopyBuffer(g_bbHandle, 1, 0, firstVisibleBar + visibleBars + 100, bbUpper);
    if(copied <= 0) return;
    
    copied = CopyBuffer(g_bbHandle, 2, 0, firstVisibleBar + visibleBars + 100, bbLower);
    if(copied <= 0) return;
    
    // 遍历所有K线
    bool isExpanding = false;
    datetime expansionStartTime = 0;
    int expansionCount = 0;
    double lastWidth = 0;
    
    for(int i = copied-1; i > 0; i--)  // 从旧到新遍历
    {
        double currentWidth = bbUpper[i] - bbLower[i];
        double widthChange = currentWidth - lastWidth;
        
        // 判断是否处于开口状态
        bool wasExpanding = isExpanding;
        isExpanding = MathAbs(widthChange) > BB_WidthThreshold;
        
        // 更新开口状态
        if(isExpanding)
        {
            if(!wasExpanding)
            {
                // 开始新的开口状态
                expansionStartTime = iTime(_Symbol, PERIOD_CURRENT, i);
                expansionCount = 1;
            }
            else
            {
                expansionCount++;
            }
        }
        else
        {
            if(wasExpanding && expansionCount >= 3)  // 最小持续3根K线
            {
                // 开口状态结束，绘制区域
                datetime endTime = iTime(_Symbol, PERIOD_CURRENT, i);
                DrawExpansionZone(expansionStartTime, endTime);
            }
            expansionCount = 0;
        }
        
        lastWidth = currentWidth;
    }
    
    // 强制更新图表
    ChartRedraw();
} 